home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-01 / memopk.zip / MEMOPACK.C < prev    next >
C/C++ Source or Header  |  1991-03-31  |  10KB  |  332 lines

  1. /*  John T. Opincar, Jr. [OpinSoft]
  2.     CID 71631,541
  3.     03/31/91
  4.     Donated to the public domain
  5.  
  6.     This module implements a "safe" and fast memo packing function.  
  7.     Memopack() is safe because it can tolerate a power loss at any time
  8.     without leaving your database/memo file in an inconsistent state.  At
  9.     worst, you'll lose ONE memo field, and only if the power loss/three-finger
  10.     salute occurs within a very short window.  Memopack() is also fast--
  11.     in tests that I ran, it ran in about 70% of the time COPY TO required.
  12.     Another benefit is that it only uses two file handles and does not 
  13.     require that you have enough disk space to make a temporary copy of the 
  14.     *.dbf file.
  15.  
  16.     In the worst case scenario, Memopack() will need the size of the 
  17.     original *.dbt file available on disk.  This occurs when you pack
  18.     a memo file which is already in optimal condition.
  19.  
  20.     To use Memopack(), simply include memopack.obj in your link file.  You
  21.     invoke memopack with the following syntax: memopack(sDbfName).
  22.  
  23.     If you are wondering how Memopack() can be "safe," (or you are skeptical
  24.     by nature) then read on.  Otherwise, you really don't need to know
  25.     anymore to use the function.  Memopack() is fault tolerant because at
  26.     any stage of its execution, the memo field pointers in the database
  27.     always point to a valid memo field.  The validity of the pointers is
  28.     preserved by using the same DBT file to store a packed copy itself.
  29.     Right after each memo is copied to the end of the DBT file, the database
  30.     pointer is updated.  The first four bytes of the DBT file contain the
  31.     block number of the next available 512 byte block in the file.  This
  32.     pointer is also updated after each memo is written to the bottom of 
  33.     the file, thus the integrity of the DBT file is preserved at all times
  34.     during this process.
  35.  
  36.     Once all of the memos have been packed to the bottom of the DBT file,
  37.     the packed memos are copied back to the beginning of the file.  Since
  38.     the memo pointers in the DBF are still pointing to the copies at the
  39.     bottom of the file, an interruption at this stage will not cause any
  40.     problems.  Of course, the DBT would then be larger than before the
  41.     pack was started, but you would just run memopack() again (hopefully
  42.     without interruption this time) to shrink it down to the minimal size.
  43.  
  44.     Once the packed memos have been copied to the top of the DBT file, 
  45.     all of the memo pointers in the DBF file are adjusted to point to the
  46.     packed copies.  The final step is to reset the pointer at the top of the
  47.     DBT file and then truncate the DBT file to its new, optimal size.
  48. */
  49. /* If you need to recompile for some reason, make sure you set it up for the
  50.    appropriate version of Clipper.
  51. */
  52. #define S87 0
  53.  
  54. #ifdef S87
  55.     #include "nandef.h"
  56. #endif
  57. #include "extend.h"
  58.  
  59. extern int _topen(byte *, int);
  60. extern int _tclose(int);
  61. extern int _tread(int, byte *, int);
  62. extern int _twrite(int, byte *, int);
  63. extern long _tlseek(int, long, int);
  64.  
  65. /*---------------------------------READNUM----------------------------------*/
  66. /*
  67. PURPOSE: Reads int or long from file
  68. RETURNS: Long
  69. */
  70.  
  71. long readnum(handle, bytecount)
  72. int handle, bytecount;
  73. {
  74.     byte abyte;
  75.     int i;
  76.     long numread = 0;
  77.  
  78.     for (i = 0; i < bytecount; i++) {
  79.         _tread(handle, &abyte, 1);
  80.         numread = numread | (((long)abyte) << (i << 3));
  81.     }
  82.     return( numread );
  83. }
  84.  
  85. /*---------------------------------WRITENUM---------------------------------*/
  86. /*
  87. PURPOSE: Writes int or long to file
  88. */
  89.  
  90. void writenum(handle, number, bytecount)
  91. int handle;
  92. long number;
  93. int bytecount;
  94. {
  95.     int i;
  96.     byte abyte;
  97.  
  98.     for (i = 0; i < bytecount; i++) {
  99.         abyte = (byte)(number & 0x000000FF);
  100.         _twrite(handle, &abyte, 1);
  101.         number = (number / 256);
  102.     }
  103.     return;
  104. }
  105.  
  106. /*----------------------------------MSTOL-----------------------------------*/
  107. /*
  108. PURPOSE: Converts hokey ASCII representation of memo block pointers in
  109.          database memo fields into a long
  110. RETURNS: Long
  111. */
  112.  
  113. long mstol(memoptrstr)
  114. byte *memoptrstr;
  115. {
  116.     int i;
  117.     long memoptr;
  118.  
  119.     for (i = 0, memoptr = 0; i <= 9; i++) {
  120.         if ( memoptrstr[i] == 32 ) {
  121.             memoptr = memoptr * 10;
  122.         } else {
  123.             memoptr = (memoptr * 10) + memoptrstr[i] - 48;
  124.         }
  125.     }
  126.     return( memoptr );
  127. }
  128.  
  129. /*----------------------------------LTOMS-----------------------------------*/
  130. /*
  131. PURPOSE: Converts long into hokey ASCII representation of memo block pointers
  132.          in database field
  133. RETURNS: Returns string in memostr
  134. */
  135.  
  136. void ltoms(memoptr, memostr)
  137. long memoptr;
  138. byte *memostr;
  139. {
  140.     int i;
  141.  
  142.     for (i = 0; i <= 9; i++) memostr[i] = ' ';
  143.     for (i = 9; (i >= 0) && (memoptr > 0); i--) {
  144.         memostr[i] = (memoptr % 10) + 48;
  145.         memoptr = memoptr / 10;
  146.     }    
  147. }
  148.  
  149. /*---------------------------------MSTRINC----------------------------------*/
  150. /*
  151. PURPOSE: Increments hokey ASCII representation of memo block pointers used
  152.          in database memo fields
  153. RETURNS: Pointer to string
  154. */
  155.  
  156. byte *mstrinc(memostr)
  157. byte *memostr;
  158. {
  159.     int i = 9, carry = 1;
  160.  
  161.     while ( carry ) {
  162.         carry = 0;
  163.         memostr[i]++;
  164.         if ( memostr[i] > '9' ) {
  165.             memostr[i--] = '0';
  166.             carry = 1;
  167.         }
  168.     }
  169.     return( memostr );
  170. }
  171.  
  172. /*---------------------------------MEMOPACK---------------------------------*/
  173. /*
  174. PURPOSE: The real McCoy.
  175. RETURNS: .T. if memo file pack was successful
  176. */
  177.  
  178. CLIPPER MEMOPACK()
  179. {
  180.     byte dbfname[13], dbtname[13], *s, *d, *t, signature;
  181.     byte fieldname[11], fieldtype, memoptrstr[10], *memoblock;
  182.     byte nextstr[10], startptrstr[10], fielddec;
  183.     int dbfhandle, dbthandle, fieldlen, offset, headersize, recordsize;
  184.     int offsets[64], memocount, curmemo, i;
  185.     long recordcount, memoptr, recordptr, currecord, firstblock, nextblock;
  186.     long blockcount, source, dest;
  187.  
  188.     /* get and fix up file names */
  189.     s = _parc(1);
  190.     d = dbfname;
  191.     t = dbtname;
  192.     while ( *s && (*s != '.') ) {
  193.         *d++ = *s;
  194.         *t++ = *s++;
  195.     }
  196.     *d++ = '.'; *d++ = 'd'; *d++ = 'b'; *d++ = 'f'; *d = '\0';
  197.     *t++ = '.'; *t++ = 'd'; *t++ = 'b'; *t++ = 't'; *t = '\0';
  198.  
  199.     /* open and verify files */
  200.     if ( (dbfhandle = _topen(dbfname, 2)) < 0 ) {
  201.         _retl(0);
  202.         return;
  203.     }
  204.     _tread(dbfhandle, &signature, 1);
  205.     if ( signature != 0x83 ) {
  206.         _tclose(dbfhandle);
  207.         _retl(0);
  208.         return;
  209.     }
  210.     if ( (dbthandle = _topen(dbtname, 2)) < 0 ) {
  211.         _tclose(dbfhandle);
  212.         _retl(0);
  213.         return;
  214.     }
  215.  
  216.     /* read some essential dbf info */
  217.        _tlseek(dbfhandle, (long)4, 0);
  218.     recordcount = readnum(dbfhandle, 4);
  219.     headersize = readnum(dbfhandle, 2);
  220.     recordsize = (int)readnum(dbfhandle, 2);
  221.  
  222.     /* allocate a read/write buffer */
  223.     memoblock = _exmgrab(512);
  224.  
  225.     /* calculate the offsets to the memo fields from the dbf header */
  226.     _tlseek(dbfhandle, (long)32, 0);
  227.     _tread(dbfhandle, fieldname, 11);
  228.     offset = 1;
  229.     memocount = -1;
  230.     while ( fieldname[0] != (char)13 ) {
  231.         _tread(dbfhandle, &fieldtype, 1);
  232.         _tlseek(dbfhandle, (long)4, 1);
  233.         _tread(dbfhandle, &fielddec, 1);
  234.         fieldlen = fielddec;
  235.         _tread(dbfhandle, &fielddec, 1);
  236.         if ( (fieldtype == 'C') && fielddec ) {
  237.             fieldlen = (fielddec << 8) | fieldlen;
  238.             fielddec = 0;
  239.         }
  240.         if ( fieldtype == 'M' ) {
  241.             offsets[++memocount] = offset;
  242.         }
  243.         offset += fieldlen;
  244.         _tlseek(dbfhandle, (long)14, 1);
  245.         _tread(dbfhandle, fieldname, 11);
  246.     }
  247.  
  248.     /* Pack everything to past the former end of the memo file, adjusting */
  249.     /* the pointers in the dbf as we go.  Also adjust the pointer in the 
  250.     /* memo file to the last block as we go. */
  251.     _tlseek(dbthandle, (long)0, 0);
  252.     firstblock = readnum(dbthandle, 4);
  253.     nextblock = firstblock;
  254.     ltoms(nextblock, nextstr);
  255.     blockcount = 0;
  256.     recordptr = headersize;
  257.     /* for each record in the dbf */
  258.     for (currecord = 0; currecord < recordcount; currecord++) {
  259.         /* for each memo field in the record */
  260.         for (curmemo = 0; curmemo <= memocount; curmemo++) {
  261.             _tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
  262.             _tread(dbfhandle, memoptrstr, 10);
  263.             memoptr = mstol(memoptrstr) << 9;
  264.             if ( memoptr > 0 ) {
  265.                 for (i = 0, s = nextstr, d = startptrstr; i <= 9; i++) *d++ = *s++;
  266.                 i = 512;
  267.                 /* for each 512 byte block allocated to this particular memo */
  268.                 while ( i >= 512 ) {
  269.                     blockcount++;
  270.                     _tlseek(dbthandle, memoptr, 0);
  271.                     _tread(dbthandle, memoblock, 512);
  272.                     _tlseek(dbthandle, nextblock << 9, 0);
  273.                     _twrite(dbthandle, memoblock, 512);
  274.                     nextblock++;
  275.                     mstrinc(nextstr);
  276.                     i = 0;
  277.                     s = memoblock;
  278.                     while ( (i < 512) && (*s != 26) ) {
  279.                         i++;
  280.                         s++;
  281.                     }
  282.                     memoptr += (long)512;
  283.                 }
  284.                 /* avoid disaster by cleaning up after each memo field */
  285.                 _tlseek(dbthandle, (long)0, 0);
  286.                 writenum(dbthandle, nextblock, 4);
  287.                 _tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
  288.                 _twrite(dbfhandle, startptrstr, 10);
  289.             }
  290.         }
  291.         recordptr += recordsize;
  292.     }
  293.  
  294.     /* Now that everything is safely packed at the end of the memo file, */
  295.     /* we have to move it back to the top.  Note that we don't adjust the */
  296.     /* memo pointers in the dbf yet */
  297.     source = firstblock << 9;
  298.     dest = (long)512;
  299.     for (i = 1; i <= blockcount; i++) {
  300.         _tlseek(dbthandle, source, 0);
  301.         _tread(dbthandle, memoblock, 512);
  302.         _tlseek(dbthandle, dest, 0);
  303.         _twrite(dbthandle, memoblock, 512);
  304.         source += (long)512;
  305.         dest += (long)512;
  306.     }
  307.  
  308.     /* Now we can adjust the memo pointers in the dbf */
  309.     recordptr = headersize;
  310.     for (currecord = 0; currecord < recordcount; currecord++) {
  311.         for (curmemo = 0; curmemo <= memocount; curmemo++) {
  312.             _tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
  313.             _tread(dbfhandle, memoptrstr, 10);
  314.             nextblock = mstol(memoptrstr);
  315.             ltoms(nextblock - firstblock + 1, memoptrstr);
  316.             _tlseek(dbfhandle, recordptr + (long)offsets[curmemo], 0);
  317.             _twrite(dbfhandle, memoptrstr, 10);
  318.         }
  319.         recordptr += recordsize;
  320.     }
  321.  
  322.     /* Finally, we gleefully chop off all of that wasted space! */
  323.     _tlseek(dbthandle, (long)0, 0);
  324.     writenum(dbthandle, (dest >> 9), 4);
  325.     _tlseek(dbthandle, dest + 1, 0);
  326.     _twrite(dbthandle, memoptrstr, 0);
  327.     _tclose(dbfhandle);
  328.     _tclose(dbthandle);
  329.     _exmback(memoblock, 512);
  330.     _retl(1);
  331. }
  332.